﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Imgur.Net.Auth;
using Imgur.Net.JSON;
using Imgur.Net.Util;
using Newtonsoft.Json;

namespace Imgur.Net
{
    /// <summary>
    /// Provides access to the album endpoint functionality of Imgur.
    /// </summary>
    /// <remarks>
    /// The album endpoint of Imgur contains the primary functionality of that service such as CRUD actions. Use of this class
    /// requires an authentication context to supply either anonymous or user owned credentials. The authentication requirement
    /// is due to the Imgur API requiring all requests to be be authenticated.
    /// </remarks>
    public class DefaultAlbumProvider : AlbumProvider
    {
        private const string DefaultUriTemplate = "https://api.imgur.com/3/album";
        private readonly string baseUriTemplate;
        private ImgurAuthProvider imgurAuthProvider;

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <remarks>
        /// This constructor uses the default API endpoint at <code>https://api.imgur.com/3/album</code> for request calls. 
        /// To change this please use one of the overloaded constructors.
        /// </remarks>
        /// <param name="authProvider">Authorization provider to privde credentials to requests made from this class.</param>
        [ExcludeFromCodeCoverage]
        public DefaultAlbumProvider(ImgurAuthProvider authProvider) : this( authProvider, DefaultUriTemplate)
        {
            
        }

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <remarks>
        /// This constructor allows specification of the base URI template to be used for request calls. In the URI, <code>{0}</code> token
        /// will be replaced where necessary by the album ID.
        /// </remarks>
        /// <param name="authProvider">Authorization provider to privde credentials to requests made from this class.</param>
        /// <param name="baseUriTemplate">Base URI template to use for future requests.</param>
        public DefaultAlbumProvider(ImgurAuthProvider authProvider, string baseUriTemplate )
        {
            this.AuthenticationProvider = authProvider;
            this.baseUriTemplate = baseUriTemplate;
        }

        /// <summary>
        /// Returns an AlbumInfo describing the album identified by the <c>id</c> parameter.
        /// </summary>
        /// <param name="id">Imgur album ID</param>
        /// <returns>ImageInfo instance containing properties of the album.</returns>
        /// <seealso cref="ImageInfo"/>
        public AlbumInfo GetAlbumInfo(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentNullException("id");

            string infoRequestTemplate = baseUriTemplate + "/{0}";
            string requestUrl = String.Format(infoRequestTemplate, id);

            WebRequest hr = WebRequest.Create(requestUrl);
            AuthenticationProvider.GetAuthorizationContext().AddContextToRequest(ref hr);

            string responseString = string.Empty;

            using (WebResponse response = hr.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader sReader = new StreamReader(responseStream))
                    {
                        responseString = sReader.ReadToEnd();
                    }
                }
            }

            JSONWrapper<AlbumInfo> responseWrapper = JsonConvert.DeserializeObject<JSONWrapper<AlbumInfo>>(responseString);

            return responseWrapper.Data;

        }

        /// <summary>
        /// Gets a collection of image infos about the images in a given album.
        /// </summary>
        /// <param name="id">The ID of the album to get image info objects for</param>
        /// <returns>IEnumerable collection of ImageInfo objects</returns>
        public IEnumerable<ImageInfo> GetAlbumImages(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentNullException("id");

            string infoRequestTemplate = baseUriTemplate + "/{0}/images";
            string requestUrl = String.Format(infoRequestTemplate, id);

            WebRequest hr = WebRequest.Create(requestUrl);
            AuthenticationProvider.GetAuthorizationContext().AddContextToRequest(ref hr);

            string responseString = string.Empty;

            using (WebResponse response = hr.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader sReader = new StreamReader(responseStream))
                    {
                        responseString = sReader.ReadToEnd();
                    }
                }
            }

            JSONWrapper<IEnumerable<ImageInfo>> responseWrapper = JsonConvert.DeserializeObject<JSONWrapper<IEnumerable<ImageInfo>>>(responseString);

            List<ImageInfo> images = new List<ImageInfo>(responseWrapper.Data);

            return images;

        }

        /// <summary>
        /// Gets basic info about an image in an album. 
        /// </summary>
        /// <param name="albumId">The ID of the album to query.</param>
        /// <param name="imageId">The ID of the image within the album.</param>
        /// <returns>Image info containg image information.</returns>
        /// <remarks>Any additional actions to be taken on an image can be found in the ImageProvider endpoint.</remarks>
        public ImageInfo GetAlbumImageInfo(string albumId, string imageId)
        {
            if (string.IsNullOrEmpty(albumId))
                throw new ArgumentException("album ID is requred.");

            if (string.IsNullOrEmpty(imageId))
                throw new ArgumentException("image ID is requred.");

            string infoRequestTemplate = baseUriTemplate + "/{0}/image/{1}";
            string requestUrl = String.Format(infoRequestTemplate, albumId, imageId);

            WebRequest hr = WebRequest.Create(requestUrl);
            AuthenticationProvider.GetAuthorizationContext().AddContextToRequest(ref hr);

            string responseString = string.Empty;

            using (WebResponse response = hr.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader sReader = new StreamReader(responseStream))
                    {
                        responseString = sReader.ReadToEnd();
                    }
                }
            }

            JSONWrapper<ImageInfo> responseWrapper = JsonConvert.DeserializeObject<JSONWrapper<ImageInfo>>(responseString);

            return responseWrapper.Data;

        }


        /// <summary>
        /// Creates a new album using the given image IDs. This method may be called by either anonymous or authenticated users.
        /// </summary>
        /// <param name="imageIds">collection of image IDs that make up the album.</param>
        /// <param name="title">title of the album.</param>
        /// <param name="description">description of the album.</param>
        /// <param name="privacy">privacy level of the album.</param>
        /// <param name="layout">layout style of the album.</param>
        /// <param name="coverImageId">image ID of the album cover image.</param>
        public AlbumInfo CreateAlbum(IEnumerable<string> imageIds, string title, string description, PrivacyLevel privacy, LayoutStyle layout, string coverImageId)
        {
            if (imageIds == null)
                throw new ArgumentException("Image IDs are required.");

            string requestUrl = baseUriTemplate;

            WebRequest hr = WebRequest.Create(requestUrl);
            AuthenticationProvider.GetAuthorizationContext().AddContextToRequest(ref hr);

            hr.Method = "POST";

            string boundary = "-------------------------------------" + DateTime.Now.Ticks;
            string newLine = Environment.NewLine;
            string kvPair = "--" + boundary + newLine +
                            "Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine + "{1}" + newLine;

            hr.ContentType = "multipart/form-data; boundary=" + boundary;

            using (Stream s = hr.GetRequestStream())
            {
                StreamWriter sw = new StreamWriter(s);

                // Write request params    
                foreach (var imageId in imageIds)
                {
                    sw.Write(kvPair, "ids[]", imageId);
                }

                if (!string.IsNullOrEmpty(title))
                {
                    string val = string.Format(kvPair, "title", title);
                    sw.Write(val);
                }

                if (!string.IsNullOrEmpty(description))
                {
                    string val = string.Format(kvPair, "description", description);
                    sw.Write(val);
                }

                sw.Write(kvPair, "privacy", privacy.ToString().ToLower());
                sw.Write(kvPair, "layout", layout.ToString().ToLower());

                if (!string.IsNullOrEmpty(coverImageId))
                {
                    string val = string.Format(kvPair, "cover", coverImageId);
                    sw.Write(val);
                }

                sw.Flush();
            }

            WebResponse response = hr.GetResponse();
            string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            JSONWrapper<AlbumInfo> responseWrapper = JsonConvert.DeserializeObject<JSONWrapper<AlbumInfo>>(responseString);

            if ( ! responseWrapper.Success )
                throw new ResponseException(responseWrapper.Status, responseWrapper.Success);

            return responseWrapper.Data;
        }

        public ImgurAuthProvider AuthenticationProvider { 
            get { return imgurAuthProvider; }
            set
            {
                imgurAuthProvider = value;
            }
        }

    }
}
